package com.asiainfo.ai.admin.utils;

import com.asiainfo.ai.admin.enums.MlpPlatformService;
import com.asiainfo.mlp.common.core.config.MlpPlatformConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import io.swagger.models.*;
import io.swagger.models.auth.ApiKeyAuthDefinition;
import io.swagger.models.auth.In;
import io.swagger.models.auth.OAuth2Definition;
import io.swagger.models.auth.SecuritySchemeDefinition;
import io.swagger.models.parameters.Parameter;
import io.swagger.models.properties.BooleanProperty;
import io.swagger.models.properties.ObjectProperty;
import io.swagger.models.properties.StringProperty;
import io.swagger.v3.core.util.Json;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriUtils;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Description: Date: 2019-05-29 17:03
 *
 * @author liyi
 */
@Slf4j
@Service
public class SwaggerDocUtil {
    private final static Pattern REF_DEF_PATTERN = Pattern.compile("\"\\$ref\":\"#/definitions/([^\"]+)\"");
    private final static Pattern REF_DEF_PATTERN2 = Pattern.compile("\"\\$ref\" : \"#/definitions/(.+)\"");

    private SwaggerDocUtil() {

    }

    private static String buildJson(String key, HttpMethod method, Operation operate, Map<String, Model> refMap) {
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode docNode = mapper.createObjectNode();
        ObjectNode pathsNode = mapper.createObjectNode();
        ObjectNode operateNode = mapper.createObjectNode();
        operateNode.putPOJO(method.name().toLowerCase(), operate);
        pathsNode.putPOJO(key, operateNode);
        docNode.putPOJO("paths", pathsNode);
        docNode.putPOJO("definitions", refMap);
        String docJson = "";
        try {
            docJson = Json.mapper().writer(new MinimalPrettyPrinter()).writeValueAsString(docNode);
        } catch (JsonProcessingException e) {
            log.warn("build swagger json error", e);
        }
        docJson = docJson.replace("«", "<").replace("»", ">");
        return codeRefJson(docJson, true);
    }

    private static String codeRefJson(String docJson, boolean isEncode) {
        StringBuffer sb = new StringBuffer(docJson.length());
        Matcher matcher = REF_DEF_PATTERN.matcher(docJson);
        while (matcher.find()) {
            String m = matcher.group();
            int start = matcher.start();
            String prefix = m.substring(0, matcher.start(1) - start).replace("$", "\\$");
            String suffix = m.substring(matcher.end(1) - start, matcher.end() - start);
            if ("String".equalsIgnoreCase(matcher.group(1))) {
                matcher.appendReplacement(sb, "\"type\" : \"string\"");
            } else {
                String newStr = isEncode ? encodeRef(matcher.group(1)) : decodeRef(matcher.group(1));
                matcher.appendReplacement(sb, prefix + newStr + suffix);
            }
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


    private static Map<String, Model> parseRefDef(Operation operate, final Map<String, Model> definitions) {
        Map<String, Model> resultMap = Maps.newHashMap();
        if (operate != null) {
            String json = Json.pretty(operate);
            Matcher matcher = REF_DEF_PATTERN2.matcher(json);
            while (matcher.find()) {
                String refId = matcher.group(1);
                Model model = definitions.get(refId);
                if (model != null) {
                    resultMap.put(refId, model);
                    resultMap.putAll(parseModelRefDef(refId, model, definitions));
                }
            }
        }
        return resultMap;
    }

    private static Map<String, Model> parseModelRefDef(String pRefId, Model model, final Map<String, Model> definitions) {
        String json = Json.pretty(model);
        Map<String, Model> resultMap = Maps.newHashMap();
        Matcher matcher = REF_DEF_PATTERN2.matcher(json);
        while (matcher.find()) {
            String refId = matcher.group(1);
            if (definitions.get(refId) != null && !refId.equals(pRefId)) {
                resultMap.put(refId, definitions.get(refId));
                resultMap.putAll(parseModelRefDef(refId, definitions.get(refId), definitions));
            }
        }
        return resultMap;
    }

    public static Map<String, SecuritySchemeDefinition> buildSecurityDefinitions(String gatewayUrl, String grantType) {
//        OAuth2Definition oauth2Def = new OAuth2Definition();
//        oauth2Def.setFlow(grantType);
//        oauth2Def.setAuthorizationUrl("http://" + gatewayUrl + "/authc/oauth/authorize");
//        oauth2Def.setTokenUrl("http://" + gatewayUrl + "/authc/oauth/token");
//        oauth2Def.setDescription("通过授权码模式,获取授权码，再通过授权码换取token");
        OAuth2Definition oauth2Def2 = new OAuth2Definition();
        oauth2Def2.setFlow("application");
        oauth2Def2.setDescription("通过客户端模式,获取访问token");
        oauth2Def2.setTokenUrl(StringUtils.removeEnd(gatewayUrl, "/") + "/uc/oauth/token");
        ApiKeyAuthDefinition bearerDef = new ApiKeyAuthDefinition();
        bearerDef.setName("Authorization");
        bearerDef.setIn(In.HEADER);
        bearerDef.setDescription(">\n" +
                "      Authorization header 必须按以下语法:\n" +
                "          `Bearer: xxxxxx.yyyyyyy.zzzzzz`");
        return ImmutableMap.of("oauth2-client", oauth2Def2, "Bearer", bearerDef);
    }

    public static Info buildApiDocInfo(MlpPlatformConfig mlpPlatformConfig) {
        Info info = new Info();
        Contact contact = new Contact();
        info.title(mlpPlatformConfig.getPlatform());
        info.description(mlpPlatformConfig.getPlatformDesc());
        info.version(mlpPlatformConfig.getPlatformVersion());
        contact.setName(mlpPlatformConfig.getPlatformLinkman());
        contact.setEmail(mlpPlatformConfig.getPlatformLinkEmail());
        info.contact(contact);
        return info;
    }

    public static Map<String, Model> buildDefaultDefinitionMap(String[] serviceId) {
        Map<String, Model> definitionMap = Maps.newHashMap();
        if (ArrayUtils.contains(serviceId, MlpPlatformService.DSEM.getCode())) {
            ModelImpl errorModel = new ModelImpl()
                    .property("processInfo", new StringProperty().description("处理信息"))
                    .property("processStatus", new StringProperty().description("处理状态"))
                    .property("success", new BooleanProperty()._default(false).description("是否处理成功"))
                    .property("value", new StringProperty().description("响应结果数据"));
            definitionMap.put("DSEM_Error", errorModel);
        } else {
            ModelImpl errorModel = new ModelImpl()
                    .property("code", new StringProperty().description("处理业务状态码"))
                    .property("message", new StringProperty().description("处理信息"))
                    .property("data", new ObjectProperty().description("响应结果数据"));
            definitionMap.put("API_Error", errorModel);
        }
        return definitionMap;
    }

    public static Map<String, Response> buildDefaultRespMap(String serviceId) {
        RefModel errorRefModel = new RefModel();
        if (MlpPlatformService.DSEM.getCode().equalsIgnoreCase(serviceId)) {
            errorRefModel.set$ref("#/definitions/DSEM_Error");
        } else {
            errorRefModel.set$ref("#/definitions/API_Error");
        }
        Map<String, Response> respMap = Maps.newHashMap();
        respMap.put("400", new Response().description("Bad Request,请求报文语法错误或参数错误").responseSchema(errorRefModel));
        respMap.put("401", new Response().description("Unauthorized,未认证,接口调用未传token,或token无效").responseSchema(errorRefModel));
        respMap.put("403", new Response().description("Forbidden,未授权禁止访问").responseSchema(errorRefModel));
        respMap.put("500", new Response().description("Internal Server Error,服务器内部错误，无法完成请求").responseSchema(errorRefModel));
        respMap.put("504", new Response().description("Gateway Time-out,请求网关超时").responseSchema(errorRefModel));
        respMap.put("503", new Response().description("Service Unavailable,服务不可用").responseSchema(errorRefModel));

        return respMap;
    }

    public static void correctParamList(List<Parameter> paramList) {
        paramList.forEach(param -> {
            if ("path".equalsIgnoreCase(param.getIn())) {
                param.setRequired(true);
            }
        });
    }

    public static String encodeRef(String str) {
        return UriUtils.encode(str, Charset.forName("UTF-8"));
    }

    public static String decodeRef(String str) {
        return UriUtils.decode(str, Charset.forName("UTF-8"));
    }
}